﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

using Caliburn.Micro;

using Action = System.Action;

namespace Haushaltsbuch.Extensions
{
    public abstract class CslaScreenBase<T> : CslaViewAware<T>, IScreen, IChild
    {
        private static readonly ILog Log = LogManager.GetLog(typeof(Screen));
        private string displayName;
        private bool isActive;
        private bool isInitialized;
        private object parent;

        /// <summary>
        ///     Indicates whether or not this instance is currently initialized.
        /// </summary>
        public bool IsInitialized
        {
            get { return this.isInitialized; }
            private set
            {
                this.isInitialized = value;
                this.NotifyOfPropertyChange("IsInitialized");
            }
        }

        /// <summary>
        ///     Creates an instance of the screen.
        /// </summary>
        public CslaScreenBase()
        {
            this.DisplayName = this.GetType().FullName;
        }

        /// <summary>
        ///     Closes this instance by asking its Parent to initiate shutdown or by asking it's corresponding view to close.
        ///     This overload also provides an opportunity to pass a dialog result to it's corresponding view.
        /// </summary>
        /// <param name="dialogResult">The dialog result.</param>
        public virtual void TryClose(bool? dialogResult)
        {
            ((Action) (() => this.GetViewCloseAction(dialogResult)())).OnUIThread();
        }

        /// <summary>
        ///     Called when initializing.
        /// </summary>
        protected virtual void OnInitialize() {}

        /// <summary>
        ///     Called when activating.
        /// </summary>
        protected virtual void OnActivate() {}

        /// <summary>
        ///     Called when deactivating.
        /// </summary>
        /// <param name="close">Inidicates whether this instance will be closed.</param>
        protected virtual void OnDeactivate(bool close) {}

        private Action GetViewCloseAction(bool? dialogResult)
        {
            var conductor = this.Parent as IConductor;
            if (conductor != null)
                return (() => conductor.CloseItem(this));
            using (Dictionary<object, object>.ValueCollection.Enumerator enumerator = this.Views.Values.GetEnumerator())
            {
                while (enumerator.MoveNext())
                {
                    object contextualView = enumerator.Current;
                    Type type = contextualView.GetType();
                    MethodInfo closeMethod = type.GetMethod("Close");
                    if (closeMethod != null)
                    {
                        return (() =>
                        {
                            bool local_0 = false;
                            if (dialogResult.HasValue)
                            {
                                local_0 = true;
                                PropertyInfo local_1 = contextualView.GetType().GetProperty("DialogResult");
                                if (local_1 != null)
                                    local_1.SetValue(contextualView, dialogResult, null);
                            }
                            if (local_0)
                                return;
                            closeMethod.Invoke(contextualView, null);
                        });
                    }
                    PropertyInfo isOpenProperty = type.GetProperty("IsOpen");
                    if (isOpenProperty != null)
                        return (() => isOpenProperty.SetValue(contextualView, false, null));
                }
            }
            return () =>
            {
                var local_0 =
                    new NotSupportedException(
                        "TryClose requires a parent IConductor or a view with a Close method or IsOpen property.");
                Log.Error(local_0);
                throw local_0;
            };
        }

        #region IChild Members

        /// <summary>
        ///     Gets or Sets the Parent <see cref="T:Caliburn.Micro.IConductor" />
        /// </summary>
        public virtual object Parent
        {
            get { return this.parent; }
            set
            {
                this.parent = value;
                this.NotifyOfPropertyChange("Parent");
            }
        }

        #endregion

        #region IScreen Members

        /// <summary>
        ///     Gets or Sets the Display Name
        /// </summary>
        public virtual string DisplayName
        {
            get { return this.displayName; }
            set
            {
                this.displayName = value;
                this.NotifyOfPropertyChange("DisplayName");
            }
        }

        /// <summary>
        ///     Indicates whether or not this instance is currently active.
        /// </summary>
        public bool IsActive
        {
            get { return this.isActive; }
            private set
            {
                this.isActive = value;
                this.NotifyOfPropertyChange("IsActive");
            }
        }

        /// <summary>
        ///     Raised after activation occurs.
        /// </summary>
        public event EventHandler<ActivationEventArgs> Activated = delegate { };

        /// <summary>
        ///     Raised before deactivation.
        /// </summary>
        public event EventHandler<DeactivationEventArgs> AttemptingDeactivation = delegate { };

        /// <summary>
        ///     Raised after deactivation.
        /// </summary>
        public event EventHandler<DeactivationEventArgs> Deactivated = delegate { };

        void IActivate.Activate()
        {
            if (this.IsActive)
                return;
            bool flag = false;
            if (!this.IsInitialized)
            {
                this.IsInitialized = flag = true;
                this.OnInitialize();
            }
            this.IsActive = true;
            Log.Info("Activating {0}.", new object[1]
                {
                    this
                });
            this.OnActivate();
            this.Activated(this, new ActivationEventArgs
                {
                    WasInitialized = flag
                });
        }

        void IDeactivate.Deactivate(bool close)
        {
            if (!this.IsActive && !this.IsInitialized)
                return;
            this.AttemptingDeactivation(this, new DeactivationEventArgs
                {
                    WasClosed = close
                });
            this.IsActive = false;
            Log.Info("Deactivating {0}.", new object[1]
                {
                    this
                });
            this.OnDeactivate(close);
            this.Deactivated(this, new DeactivationEventArgs
                {
                    WasClosed = close
                });
            if (!close)
                return;
            this.Views.Clear();
            Log.Info("Closed {0}.", new object[1]
                {
                    this
                });
        }

        /// <summary>
        ///     Called to check whether or not this instance can close.
        /// </summary>
        /// <param name="callback">The implementor calls this action with the result of the close check.</param>
        public virtual void CanClose(Action<bool> callback)
        {
            callback(true);
        }

        /// <summary>
        ///     Tries to close this instance by asking its Parent to initiate shutdown or by asking its corresponding view to close.
        /// </summary>
        public void TryClose()
        {
            ((Action) (() => this.GetViewCloseAction(new bool?())())).OnUIThread();
        }

        #endregion
    }
}